import numpy as np
import math as m
import pandas as pd
import statsmodels.api as sm
from warnings import simplefilter
from scipy.stats import norm

class RSR:
    def __init__(self, DecisionMatrix, weight, q):
        '''
        :param DecisionMatrix: 决策矩阵
        :param wight: 属性权重
        :param q: q值
        '''
        self.matrix = DecisionMatrix
        self.weight = weight
        self.q = q

    def addition(self,Ivq1,Ivq2,q):
        '''
        加法
        :param Ivq1:区间值
        :param Ivq2:区间值
        :param q:q值
        :return:
        '''
        a,b,c,d=Ivq1[0][0],Ivq1[0][1],Ivq1[1][0],Ivq1[1][1]
        e,f,g,h=Ivq2[0][0],Ivq2[0][1],Ivq2[1][0],Ivq2[1][1]
        u1=a**q+e**q-(a**q)*(e**q)
        u2=b**q+f**q-(b**q)*(f**q)
        v1=c*g
        v2=h*d
        return ([u1,u2],[v1,v2])

    def subtraction(self,Ivq1,Ivq2,q):
        '''
        减法
        :param Ivq1:区间值
        :param Ivq2:区间值
        :param q:q值
        :return:
        '''
        a,b,c,d=Ivq1[0][0],Ivq1[0][1],Ivq1[1][0],Ivq1[1][1]
        e,f,g,h=Ivq2[0][0],Ivq2[0][1],Ivq2[1][0],Ivq2[1][1]
        u1=a*g
        u2=b*h
        v1=(c**q+e**q-(c**q)*(e**q))**(1/q)
        v2=(d**q+f**q-(d**q)*(f**q))**(1/q)
        return ([u1, u2],[v1, v2])

    def multiplication(self,Ivq1,Ivq2,q):
        '''
        乘法
        :param Ivq1:区间值
        :param Ivq2:区间值
        :param q:q值
        :return:
        '''
        a, b, c, d = Ivq1[0][0], Ivq1[0][1], Ivq1[1][0], Ivq1[1][1]
        e, f, g, h = Ivq2[0][0], Ivq2[0][1], Ivq2[1][0], Ivq2[1][1]
        u1 = a * e
        u2 = b * f
        v1 = (c ** q + g ** q - (c ** q) * (g ** q)) ** (1 / q)
        v2 = (d ** q + h ** q - (d ** q) * (h ** q)) ** (1 / q)
        return ([u1, u2], [v1, v2])

    def division(self,Ivq1,Ivq2,q):
        '''
        除法
        :param Ivq1:区间值
        :param Ivq2:区间值
        :param q:q值
        :return:
        '''
        a, b, c, d = Ivq1[0][0], Ivq1[0][1], Ivq1[1][0], Ivq1[1][1]
        e, f, g, h = Ivq2[0][0], Ivq2[0][1], Ivq2[1][0], Ivq2[1][1]
        u1 = (a ** q + g ** q - (a ** q) * (g ** q)) ** (1 / q)
        u2 = (b ** q + h ** q - (b ** q) * (h ** q)) ** (1 / q)
        v1 = c*e
        v2 = d*f
        return ([u1, u2], [v1, v2])

    def getDist(self,Ivq1,Ivq2,q):
        '''
        求解区间值广义正交模糊数lvq1,lvq2之间的距离
        :param Ivq1:区间值
        :param Ivq2:区间值
        :param q:q值
        '''
        a, b, c, d = Ivq1[0][0], Ivq1[0][1], Ivq1[1][0], Ivq1[1][1]
        e, f, g, h = Ivq2[0][0], Ivq2[0][1], Ivq2[1][0], Ivq2[1][1]
        D = 1/4*(abs(a**q - e**q - c**q + g**q) + abs(b**q - f**q - d**q + h**q))
        return D


    def Exponentiation(self,Ivq1,q,n):
        '''
        次方运算
        :param Ivq1:
        :param q:
        :param n:开多少次方
        :return:
        '''
        a, b, c, d = Ivq1[0][0], Ivq1[0][1], Ivq1[1][0], Ivq1[1][1]
        u1 = a ** n
        u2 = b ** n
        v1 =(1-((1-(c**q))**n))**(1/q)
        v2 =(1-((1-(d**q))**n))**(1/q)
        return ([u1, u2], [v1, v2])

    def getPosNegPoint(self, NormalizedMatrix):
        '''
        求正负理想解
        :param NormalizedMatrix: 区间值矩阵
        :param q: q值
        :return: 传入区间值矩阵的贴近度矩阵
        '''
        Row, Column = len(NormalizedMatrix), len(NormalizedMatrix[0])
        for i in range(Row):
            Positiveline, Negativeline = [], []
            for j in range(Column):
                ##求理想点
                Positiveidu1 = np.max((((NormalizedMatrix[:, j])[:, 0])[:, 0]))
                Positiveidu2 = np.max((((NormalizedMatrix[:, j])[:, 0])[:, 1]))
                Positiveidv1 = np.min((((NormalizedMatrix[:, j])[:, 1])[:, 0]))
                Positiveidv2 = np.min((((NormalizedMatrix[:, j])[:, 1])[:, 1]))
                Negativeidu1 = np.min((((NormalizedMatrix[:, j])[:, 0])[:, 0]))
                Negativeidu2 = np.min((((NormalizedMatrix[:, j])[:, 0])[:, 1]))
                Negativeidv1 = np.max((((NormalizedMatrix[:, j])[:, 1])[:, 0]))
                Negativeidv2 = np.max((((NormalizedMatrix[:, j])[:, 1])[:, 1]))
                Positiveline.append(([Positiveidu1, Positiveidu2], [Positiveidv1, Positiveidv2]))
                Negativeline.append(([Negativeidu1, Negativeidu2], [Negativeidv1, Negativeidv2]))

        return Positiveline, Negativeline

    def getscore(self,IVq):
        '''
        我们提出的得分函数
        :param IVq:区间值
        :param q:q值
        :return:
        '''
        a = IVq[0][0]
        b = IVq[0][1]
        c = IVq[1][0]
        d = IVq[1][1]
        return (m.log(a + b + c + d + 1) + (b - a) + (d - c) ** 2 + (a - c + b - d) * m.log(3) / 2) / (2 * m.log(3))

    def getPosNor(self, Ivq, Pos, Neg,q):
        '''
        对正类型的指标进行处理
        :param Ivq: 区间值
        :param Pos: 正理想解元素
        :param Neg: 负理想解元素
        :param q: q值
        :return:
        '''
        PosNor=self.division(self.subtraction(Ivq,Neg,q),self.subtraction(Pos,Neg,q),q)
        return PosNor


    def getNegNor(self, Ivq, Pos, Neg,q):
        '''
        对负类型指标进行处理
        题中用的是+1，本处改成+正理想元素
        :param Ivq:区间值
        :param Pos: 正理想解元素
        :param Neg: 负理想解元素
        :param q: q值
        :return:
        '''

        t=self.division(self.subtraction(Neg,Ivq,q),self.subtraction(Pos,Neg,q),q)
        NegNor=self.addition(t,Pos,q)
        return NegNor

    def NormalizedMatrix(self,Matrix,Positiveline, Negativeline,q):
        '''
        规范化矩阵
        :param Matrix:决策矩阵
        :param q:q值
        :return:Matrix规范化之后的矩阵
        '''
        costindex = (0,1)  # 表示哪一列是负指标,从0开始(剩下的是正指标) 这个根据实际情况确定
        #print(f'原矩阵{Matrix.round(4)}')
        for i in range(len(Matrix)):
            for j in range(len(Matrix[0])):
                if j in costindex:
                    Matrix[i][j] = self.getNegNor(Matrix[i][j], Positiveline[j], Negativeline[j], q)
                else:
                    Matrix[i][j] = self.getPosNor(Matrix[i][j], Positiveline[j], Negativeline[j], q)
        #print(f'标准化之后{Matrix.round(4)}')
        return Matrix

    def getMarginalUtilityScore(self,NormalizedMatrix):
        '''
        获取边际效用得分
        :param NormalizedMatrix:规范化矩阵
        :return:
        '''
        ScoreMatrix=[]
        for Row in range(len(NormalizedMatrix)):
            Scoreline=[]
            for Column in range(len(NormalizedMatrix[0])):
                t=self.getscore(NormalizedMatrix[Row][Column])
                Scoreline.append(t)
            ScoreMatrix.append(Scoreline)
        return ScoreMatrix



    def getFinalUtilityScore(self,ScoreMatrix,Weight):
        '''
        获取最终效用得分
        :param ScoreMatrix: 得分矩阵
        :param Weight: 权重
        :return:
        '''
        U = []
        for Row in range(len(ScoreMatrix)):
            u0=0
            for Column in range(len(ScoreMatrix[0])):
                u0+=Weight[Column]*ScoreMatrix[Row][Column]
            U.append(u0)
        return U

    def getResult(self):
        '''
        最终结果
        :param Matrix:
        :param q:
        :return:
        '''
        Positiveline, Negativeline=self.getPosNegPoint(self.matrix)
        NormalizedMatrix=self.NormalizedMatrix(Matrix, Positiveline, Negativeline, self.q)

        ScoreMatrix=self.getMarginalUtilityScore(NormalizedMatrix)
        df = pd.DataFrame(ScoreMatrix,columns=["X1", "X2", "X3", "X4", "X5"])

        # 正向指标,从小到大排序
        df["R1"] = df["X1"].rank(ascending=True, method="average")
        df["R2"] = df["X2"].rank(ascending=True, method="average")
        df["R3"] = df["X3"].rank(ascending=True, method="average")
        # 负向指标,从大到小排
        df["R4"] = df["X4"].rank(ascending=False, method="average")
        df["R5"] = df["X5"].rank(ascending=False, method="average")
        # 计算RSR值
        df["RSR"] = df.loc[:, ["R1", "R2", "R3", "R4", "R5"]].sum(axis=1) / (5 * 10)
        # 排序
        df.sort_values(by="RSR", inplace=True)
        # 因为实际数据没有重复,此处偷懒,直接写1
        df["f"] = 1
        df["Σf"] = df["RSR"].rank(method="average")
        df["R/n"] = df["Σf"] / len(df)
        df["R/n'"] = df["R/n"]
        df.iat[-1, -1] = 1 - 1 / 4 / len(df)

        df["Probit"] = norm.ppf(df["R/n'"])
        df["Probit'"] = df["Probit"] + 5

        r0 = np.polyfit(df["Probit'"], df["RSR"], deg=1)
        # if r0[1] > 0:
        #     print(f"\n回归直线方程为：y = {r0[0]} Probit + {r0[1]}")
        # else:
        #     print(f"\n回归直线方程为：y = {r0[0]} Probit - {abs(r0[1])}")

        simplefilter(action="ignore", category=FutureWarning)
        simplefilter(action="ignore", category=UserWarning)
        # print(sm.OLS(df["Probit"], sm.add_constant(df["RSR"])).fit().summary())
        df["regression"] = np.polyval(r0, df["Probit'"])

        C_LEVEL = norm.ppf(0.4) + 5
        B_LEVEL = norm.ppf(0.8) + 5
        A_LEVEL = 10

        threshold = np.polyval(r0, [0, C_LEVEL, B_LEVEL, A_LEVEL])
        # print("threshold:", threshold)
        df["level"] = pd.cut(df["regression"], threshold, labels=["C", "B", "A"])
        df["regression_pct"] = df["regression"].rank(pct=True, ascending=True, method="average")
        # 设置pandas打印时显示的列数
        pd.set_option("display.max_columns", 1000)
        pd.set_option("display.max_columns", None)
        pd.set_option("display.width", 1000)

        Result = np.array(df['regression']).tolist()
        Result=[i/sum(Result) for i in Result]
        return Result




if __name__ == "__main__":
    Matrix = np.array([[([0.58, 0.75], [0.1, 0.2]), ([0.7, 0.78], [0.2, 0.3]), ([0.5, 0.78], [0.1, 0.2]),
                        ([0.65, 0.7], [0.3, 0.35]), ([0.4, 0.5], [0.5, 0.6])],
                       [([0.5, 0.6], [0.1, 0.2]), ([0.75, 0.78], [0.2, 0.3]), ([0.6, 0.78], [0.1, 0.15]),
                       ([0.7, 0.8], [0.2, 0.3]), ([0.5, 0.6], [0.4, 0.5])],
                       [([0.5, 0.78], [0.1, 0.2]), ([0.6, 0.7], [0.3, 0.4]), ([0.7, 0.78], [0.2, 0.3]), ([0.6, 0.7], [0.3, 0.4]),
                       ([0.35, 0.45], [0.5, 0.65])],
                       [([0.75, 0.780], [0.2, 0.3]), ([0.6, 0.75], [0.2, 0.3]), ([0.7, 0.78], [0.2, 0.3]),
                       ([0.55, 0.65], [0.3, 0.4]), ([0.35, 0.4], [0.6, 0.65])],
                       [([0.6, 0.75], [0.3, 0.4]), ([0.5, 0.6], [0.4, 0.5]), ([0.6, 0.7], [0.3, 0.4]), ([0.5, 0.6], [0.4, 0.5]),
                       ([0.1, 0.2], [0.4, 0.5])]])
    weight = (0.2, 0.2, 0.2, 0.2, 0.2)

    rsr = RSR(Matrix, weight, 3)

    result = rsr.getResult()
    print(f'U={np.round(result,4)}')

